﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Dalamud_UserUpdate.Services;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.Command;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using Lumina.Excel.Sheets;
using Newtonsoft.Json;

namespace Dalamud_UserUpdate
{
    public class Plugin : IDalamudPlugin
    {
        public string Name => "Dalamud User Update";
        private const string CommandName = "/userupdate";

        private readonly Configuration _configuration;

        private readonly Dictionary<ushort, TerritoryType> _territories;
        private readonly Dictionary<uint, World> _worlds;

        // Use a static HttpClient to avoid socket exhaustion.
        private static readonly HttpClient HttpClient = new();

        // Data for the current zone. Cleared on zone change.
        // Using HashSet for efficient duplicate checking (O(1) average time complexity).
        private readonly HashSet<ulong> _seenPlayerIds = new();
        private readonly List<FFXIVPlayObjectUser> _playerDataForUpload = new();

        public Plugin(IDalamudPluginInterface pluginInterface)
        {
            pluginInterface.Create<Service>();

            _configuration = Service.Interface.GetPluginConfig() as Configuration ?? new Configuration();
            _configuration.Initialize(Service.Interface);

            _territories = Service.DataManager.GetExcelSheet<TerritoryType>()!.ToDictionary(t => (ushort)t.RowId, t => t);
            _worlds = Service.DataManager.GetExcelSheet<World>()!.ToDictionary(t => t.RowId, t => t);

            Service.Framework.Update += OnFrameworkUpdate;
            Service.ClientState.TerritoryChanged += OnTerritoryChanged;
            Service.CommandManager.AddHandler(CommandName, new CommandInfo(OnCommand) { HelpMessage = "Open configuration window." });
            Service.Interface.UiBuilder.Draw += DrawUI;
            Service.Interface.UiBuilder.OpenConfigUi += OpenConfigUi;
        }

        public void Dispose()
        {
            Service.Interface.UiBuilder.OpenConfigUi -= OpenConfigUi;
            Service.Interface.UiBuilder.Draw -= DrawUI;
            Service.CommandManager.RemoveHandler(CommandName);
            Service.ClientState.TerritoryChanged -= OnTerritoryChanged;
            Service.Framework.Update -= OnFrameworkUpdate;
            _configuration.Save();
        }

        private void OnCommand(string command, string args)
        {
            OpenConfigUi();
        }

        private void OnFrameworkUpdate(IFramework framework)
        {
            if (!Service.ClientState.IsLoggedIn || Service.ClientState.LocalPlayer == null)
            {
                return;
            }

            var localPlayer = Service.ClientState.LocalPlayer;

            // Using a lock on the object table is good practice when iterating.
            lock (Service.ObjectTable)
            {
                foreach (var gameObject in Service.ObjectTable)
                {
                    if (gameObject.ObjectKind != ObjectKind.Player || gameObject.GameObjectId == localPlayer.GameObjectId)
                    {
                        continue;
                    }

                    // Use the more specific ContentId for uniqueness if available and preferred.
                    // GameObjectId is also a good candidate. Using ContentId as it was in the original unsafe block.
                    var contentId = Utils.getContentId(gameObject);
                    if (contentId == 0 || _seenPlayerIds.Contains(contentId))
                    {
                        continue;
                    }

                    if (gameObject is IPlayerCharacter playerCharacter)
                    {
                        // Mark player as seen in this zone.
                        _seenPlayerIds.Add(contentId);

                        // Gather data for upload.
                        var world = _worlds.GetValueOrDefault(playerCharacter.HomeWorld.RowId);
                        var territory = _territories.GetValueOrDefault(Service.ClientState.TerritoryType);

                        if (world.RowId != 0 && territory.RowId != 0)
                        {
                            var user = FFXIVPlayObjectUser.GetFfxivPlayObjectUser(localPlayer, playerCharacter, world, territory);
                            _playerDataForUpload.Add(user);
                        }
                    }
                }
            }
        }

        private void OnTerritoryChanged(ushort territoryId)
        {
            if (!_playerDataForUpload.Any())
            {
                // No data to upload, just clear lists for the new zone.
                _seenPlayerIds.Clear();
                return;
            }

            // Copy data to a temporary list for uploading.
            var dataToUpload = new List<FFXIVPlayObjectUser>(_playerDataForUpload);

            // Clear the main lists immediately to start fresh in the new zone.
            _seenPlayerIds.Clear();
            _playerDataForUpload.Clear();

            if (_configuration.EnableChatLogging)
            {
                Service.ChatGui.Print($"[UserUpdate] Will upload {dataToUpload.Count} player(s) data.");
            }
            
            if(territoryId is 179)
            {
                if(dataToUpload.Count <= 7)
                {
                    return;
                }
            }
            

            // foreach (var ffxivPlayObjectUser in dataToUpload)
            {
                // Perform the upload in the background.
                Task.Run
                (
                    async () =>
                    {
                        try
                        {
                            
                            var dataToUploadCopy = new List<FFXIVPlayObjectUser>(dataToUpload);
                            
                            const int batchSize = 100;
                            for (var i = 0; i < dataToUploadCopy.Count; i += batchSize)
                            {
                                var batch = dataToUploadCopy.Skip(i).Take(batchSize).ToList();
                                await UploadData(batch);
                                
                                // 避免请求过于密集，添加短暂延迟
                                if (i + batchSize < dataToUploadCopy.Count)
                                {
                                    await Task.Delay(100);
                                }
                            }
                            
                            if (_configuration.上传失败时发送消息)
                            {
                                // Service.ChatGui.Print($"[UserUpdate] Successfully uploaded data for {ffxivPlayObjectUser.nickname}.");
                            }
                            dataToUpload.Clear();
                            dataToUploadCopy.Clear();
                        }
                        catch (Exception ex)
                        {
                            if (_configuration.上传失败时发送消息)
                            {
                                // Service.ChatGui.Print($"[UserUpdate] Successfully uploaded data for {ffxivPlayObjectUser.nickname}.");
                                Service.PluginLog.Error($"上传报错了: {ex.Message}");
                            }
                            
                         
                            // Service.PluginLog.Error($"[UserUpdate] Failed to upload player data for {ffxivPlayObjectUser.nickname}. Error: {ex.Message}");
                        }
                    }
                );
            }
        }

        private static async Task UploadData(List<FFXIVPlayObjectUser> userData)
        {
            
            Service.PluginLog.Info($"开始上传: {userData.Count} 条数据");
            
            // The original project sent one request per user.
            // A much better approach is to send all data in a single request if the server supports it.
            // Assuming the server can take an array of users.
            const string url = "https://ff14web.dyos.top/app/Api/listAddByNet";
            // const string url = "http://127.0.0.1:18861/Api/listAddByNet";

            var jsonPayload = JsonConvert.SerializeObject(userData);
            using var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");

            var response = await HttpClient.PostAsync(url, content);
            response.EnsureSuccessStatusCode();
        }

        private void DrawUI()
        {
            if (!_configuration.ConfigWindowVisible)
            {
                return;
            }
            
            if (ImGui.Begin(Name, ref _configuration.ConfigWindowVisible, ImGuiWindowFlags.AlwaysAutoResize))
            {
                ImGui.BeginTabBar("Settings");
                if (ImGui.BeginTabItem("上传设置"))
                {
                    上传模块UI();
                    ImGui.EndTabItem();
                }
                
                if (ImGui.BeginTabItem("Debug"))
                {
                    DebugUI();
                    ImGui.EndTabItem();
                }
                ImGui.EndTabBar();
                
            }
            ImGui.End();
        }

        private void DebugUI()
        {
            var clientState = Service.ClientState;
            var clientStateLocalPlayer = clientState.LocalPlayer;
            if (clientStateLocalPlayer != null)
            {
                ImGui.Text($"目前数量:{_seenPlayerIds.Count}");
                ImGui.Text($"TerritoryType:{clientState.TerritoryType}");
                ImGui.Text($"GameObjectId:{clientStateLocalPlayer.GameObjectId}-{clientStateLocalPlayer.GameObjectId:X}");
                ImGui.Text($"ContentId:{Utils.getContentId(clientStateLocalPlayer)}-{Utils.getContentId(clientStateLocalPlayer):X}");
                
                // foreach (var ffxivPlayObjectUser in _playerDataForUpload)
                // {
                //     ImGui.Text($"{ffxivPlayObjectUser.nickname} -{ffxivPlayObjectUser.ContentId} -{ffxivPlayObjectUser.ContentId:X}");
                // }
                //
            }
        }

        private void 上传模块UI()
        {
            ImGui.Checkbox("Enable chat logging", ref _configuration.EnableChatLogging);
            if (ImGui.IsItemHovered())
            {
                ImGui.SetTooltip("Logs upload activity to the chat window.");
            }

            ImGui.Checkbox("上传失败时发送消息", ref _configuration.上传失败时发送消息);
            if (ImGui.IsItemHovered())
            {
                ImGui.SetTooltip("Logs upload activity to the chat window.");
            }
        }

        private void OpenConfigUi()
        {
            _configuration.ConfigWindowVisible = true;
        }
    }
}